using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Shapes;

using System.Windows.Controls;
using WozLib.UI.Ink.SketchProperties;
using System.Windows.Media;
using System.Windows;
using System.Diagnostics;
using System.Windows.Markup;
using WozLib.UI.Ink.SketchObjects;

namespace WozLib.UI.Ink.SketchElements
{
  public class SketchLine : Canvas, ISketchObject
  {
    public static SketchLine FromLineWozObject(LineSketchObject lineWozObject)
    {
      SketchPropertyCollection wozPropertyCollection = new SketchPropertyCollection();
      wozPropertyCollection.WozProperties = lineWozObject.WozProperties;

      return new SketchLine(lineWozObject.WozID, wozPropertyCollection);
    }

    #region old
    //protected override Geometry DefiningGeometry
    //{
    //  get
    //  {
    //    LineGeometry myLineGeometry = new LineGeometry();
    //    myLineGeometry.StartPoint = new Point(Line.X1, Line.Y1);
    //    myLineGeometry.EndPoint = new Point(Line.X2, Line.Y2);
    //    return myLineGeometry;
    //  }
    //}

    //public override void BeginInit()
    //{
    //  base.BeginInit();
    //  Line.BeginInit();
    //}

    //public override void EndInit()
    //{
    //  base.EndInit();
    //  Line.EndInit();
    //}

    //public override Transform GeometryTransform
    //{
    //  get
    //  {
    //    return Line.GeometryTransform;
    //  }
    //}

    //public override Geometry RenderedGeometry
    //{
    //  get
    //  {
    //    return Line.RenderedGeometry;
    //  }
    //}

    //protected override Size ArrangeOverride(Size finalSize)
    //{
    //  return finalSize;
    //}


    //protected override Geometry GetLayoutClip(Size layoutSlotSize)
    //{
    //  Geometry geometry = base.GetLayoutClip(layoutSlotSize);
    //  return geometry;
    //}

    //protected override Size MeasureOverride(Size constraint)
    //{
    //  Size size = base.MeasureOverride(constraint);
    //  return size;
    //}

    
    #endregion

    private static Line GetDefaultLine(Canvas canvas)
    {
      return GetDefaultLine(new Point(0.0, 0.0), new Point(1.0, 1.0), canvas);
    }

    private static Line GetDefaultLine(Point startPoint, Point endPoint, Canvas canvas)
    {
      

      Line line = new Line();
      line.Stroke = Brushes.Black;
      line.StrokeThickness = 2.0;
      //if (startPoint.Y > endPoint.Y)
      //{
      //  canvas.SetValue(InkCanvas.LeftProperty, startPoint.X);
      //  canvas.SetValue(InkCanvas.TopProperty, endPoint.Y);
      //  canvas.Width = (endPoint.X - startPoint.X);
      //  canvas.Height = (startPoint.Y - endPoint.Y);
      //  line.X1 = 0;
      //  line.Y1 = canvas.Height;
      //  line.X2 = canvas.Width;
      //  line.Y2 = 0;
      //}
      //else
      //{
      //  canvas.SetValue(InkCanvas.LeftProperty, startPoint.X);
      //  canvas.SetValue(InkCanvas.TopProperty, startPoint.Y);
      //  canvas.Width = (endPoint.X - startPoint.X);
      //  canvas.Height = (endPoint.Y - startPoint.Y);
      //  line.X1 = 0;
      //  line.Y1 = 0;
      //  line.X2 = canvas.Width;
      //  line.Y2 = canvas.Height;
      //}
      
      canvas.Children.Add(line);
      return line;
    }

    private Line _Line;

    public Line Line
    {
      get { return _Line; }
      set { _Line = value; }
    }

    public SketchLine()
      : this(new Point(0.0,0.0),new Point(1.0,1.0),null)
    {
    }

    public SketchLine(Point startPoint, Point endPoint, SketchObjectRegistry wozObjectRegistry)
    {

      // might need to reverse these
      if (startPoint.X > endPoint.X)
      {
        Point newStart = endPoint;
        endPoint = startPoint;
        startPoint = newStart;
      }

      Line = GetDefaultLine(startPoint, endPoint, this);

      // register this object
      if (wozObjectRegistry != null)
      {
        wozObjectRegistry.RegisterObject(this);
      }
      else
      {
        
      }

      // Set all of our WozPropertyCollection properties

      // Set color property
      if (Line.Stroke is SolidColorBrush)
      {
        ColorSketchProperty colorWozProperty = new ColorSketchProperty((Line.Stroke as SolidColorBrush).Color, WozPropertyType.StrokeColor);
        this.SetProperty(colorWozProperty);
      }

      //WozBoundsProperty wozBoundsProperty = new WozBoundsProperty(this.GetBounds());
      //_WozPropertyCollection.SetProperty(wozBoundsProperty);

      // set the stroke thickness
      InkThicknessSketchProperty wozThicknessProperty = new InkThicknessSketchProperty(new Thickness(Line.StrokeThickness));
      this.SetProperty(wozThicknessProperty);

      // set the points
      LineGeometrySketchProperty lineGeometryWozProperty = new LineGeometrySketchProperty(startPoint, endPoint, WozPropertyType.LineGeometry);
      this.SetProperty(lineGeometryWozProperty);

      // set whether the stroke is dashed
      BooleanSketchProperty strokeDashed = new BooleanSketchProperty(false, WozPropertyType.StrokeDashed);
      this.SetProperty(strokeDashed);

      Subscribe();
    }

    public SketchLine(long id, SketchPropertyCollection wozPropertyCollection)
      : this(id, wozPropertyCollection, null)
    {
    }

    public SketchLine(long id, SketchPropertyCollection wozPropertyCollection, SketchObjectRegistry wozObjectRegistry)
      : base()
    {

      Line = GetDefaultLine(this);

      // assign our ID before we doing anything else
      this._WozID = id;

      // overwrite our blank collection with the provided one
      this._WozPropertyCollection = wozPropertyCollection;

      // all of our properties can be set this way
      SetProperty(wozPropertyCollection.WozProperties);

      // register this object
      if (wozObjectRegistry != null)
      {
        wozObjectRegistry.RegisterObjectWithExisitingId(this);
      }
      Subscribe();
    }

    private void Subscribe()
    {
      this.SizeChanged += new SizeChangedEventHandler(WozLine_SizeChanged);
    }

    private bool _sizeChanging = false;
    void WozLine_SizeChanged(object sender, SizeChangedEventArgs e)
    {
      if (!_sizeChanging)
      {
        _sizeChanging = true;
        //LineGeometryWozProperty line = (wozProperty as LineGeometryWozProperty);
        //this.SetValue(InkCanvas.LeftProperty, line.StartPoint.X);
        //this.SetValue(InkCanvas.TopProperty, line.StartPoint.Y);

        //if (this.Line.Y1 > this.Line.Y2)
        //{
        //  //this.Line.X2 = this.Width;
        //  //this.Line.Y1 = this.Height;

        //  LineGeometryWozProperty lineGeometryWozProperty = new LineGeometryWozProperty(
        //    new Point((double)this.GetValue(InkCanvas.LeftProperty),
        //    ((double)this.GetValue(InkCanvas.TopProperty)) + (this.Line.Y1 - this.Line.Y2)), 
        //    new Point(this.Width + (double)this.GetValue(InkCanvas.LeftProperty), this.Line.Y2 + (double)this.GetValue(InkCanvas.TopProperty)),
        //    WozPropertyType.LineGeometry);
        //  UpdateProperty(lineGeometryWozProperty);
        //}
        //else
        //{
        //  LineGeometryWozProperty lineGeometryWozProperty = new LineGeometryWozProperty(
        //    new Point((double)this.GetValue(InkCanvas.LeftProperty),(double)this.GetValue(InkCanvas.TopProperty)),
        //    new Point(this.Width + (double)this.GetValue(InkCanvas.LeftProperty), this.Height + (double)this.GetValue(InkCanvas.TopProperty)), 
        //    WozPropertyType.LineGeometry);
        //  UpdateProperty(lineGeometryWozProperty);
        //  //this.Line.X2 = this.Width;
        //  //this.Line.Y2 = this.Height;
        //}
        _sizeChanging = false;
      }
      
      //this.Width = (line.EndPoint.X - line.StartPoint.X);
      //this.Height = (line.EndPoint.Y - line.StartPoint.Y);
    }

    #region IWozObject Members

    // our private way of holding an ID
    private long _WozID = long.MinValue;

    public long WozID
    {
      get 
      {
        return _WozID;
      }
      set
      {
        _WozID = value;
      }
    }

    // our private way of holding properties
    protected SketchPropertyCollection _WozPropertyCollection = new SketchPropertyCollection();

    // You should update your properties to reflect the actualy state of your underlying object
    // i.e if your current color property says Black, but your stroke actually had its color
    // changed to Red recently - change the color property to red
    public void SyncProperties()
    {
      SyncProperties(_WozPropertyCollection);
    }

    // You should update your properties to reflect the actualy state of your underlying object
    // i.e if your current color property says Black, but your stroke actually had its color
    // changed to Red recently - change the color property to red
    protected void SyncProperties(SketchPropertyCollection wozPropertyCollection)
    {

      // to prevent NaN bug in InkCanvas resizing code
      // we temporarily set the size back to zero and let UpdateProperty change it back to the special 0.12345 representation
      if (this.Width == 0.12345)
      {
        this.Width = 0;
      }

      if (this.Height == 0.12345)
      {
        this.Height = 0;
      }

      if (this.Line.Y1 > this.Line.Y2)
      {
        //this.Line.X2 = this.Width;
        //this.Line.Y1 = this.Height;

        LineGeometrySketchProperty lineGeometryWozProperty = new LineGeometrySketchProperty(
          new Point((double)this.GetValue(InkCanvas.LeftProperty),
          ((double)this.GetValue(InkCanvas.TopProperty)) + (this.Height)),
          new Point(this.Width + (double)this.GetValue(InkCanvas.LeftProperty), (double)this.GetValue(InkCanvas.TopProperty)),
          WozPropertyType.LineGeometry);
        SetProperty(lineGeometryWozProperty);
      }
      else
      {
        LineGeometrySketchProperty lineGeometryWozProperty = new LineGeometrySketchProperty(
          new Point((double)this.GetValue(InkCanvas.LeftProperty), (double)this.GetValue(InkCanvas.TopProperty)),
          new Point(this.Width + (double)this.GetValue(InkCanvas.LeftProperty), this.Height + (double)this.GetValue(InkCanvas.TopProperty)),
          WozPropertyType.LineGeometry);

        SetProperty(lineGeometryWozProperty);
        //this.Line.X2 = this.Width;
        //this.Line.Y2 = this.Height;
      }
      
    }

    // Set the property, but also make the corresponding change to the underlying object
    // i.e. if the color property is set on a stroke, actually change the drawing attributes
    public void SetProperty(WozLib.UI.Ink.SketchProperties.SketchProperty wozProperty)
    {
      _WozPropertyCollection.SetProperty(wozProperty);

      UpdateProperty(wozProperty);
    }

    private void UpdateProperty(WozLib.UI.Ink.SketchProperties.SketchProperty wozProperty)
    {

      try
      {
        // Figure out what we should with this property
        switch (wozProperty.WozPropertyType)
        {
          case WozPropertyType.StrokeColor:
            Line.Stroke = new SolidColorBrush((wozProperty as ColorSketchProperty).Data);
            break;
          case WozPropertyType.StrokeDashed:
            // if true
            // StrokeDashArray="2"
            if ((wozProperty as BooleanSketchProperty).Data)
            {
              Line.StrokeDashArray = new DoubleCollection(new double[] { 2.0 });
            }
            else
            {
              Line.StrokeDashArray = null;
            }
            break;
          case WozPropertyType.InkThickness:
            Line.StrokeThickness = (wozProperty as InkThicknessSketchProperty).Data.Left;
            break;
          case WozPropertyType.LineGeometry:
            // WATCH OUT FOR RECURSIVE STACK OVERFLOW HERE
            _sizeChanging = true;
            LineGeometrySketchProperty line = (wozProperty as LineGeometrySketchProperty);
            if (line.StartPoint.Y > line.EndPoint.Y)
            {
              //this.Line.X2 = this.Width;
              //this.Line.Y1 = this.Height;

              this.SetValue(InkCanvas.LeftProperty, line.StartPoint.X);
              this.SetValue(InkCanvas.TopProperty, line.EndPoint.Y);
              this.Line.X2 = line.EndPoint.X - line.StartPoint.X;
              this.Line.Y1 = line.StartPoint.Y - line.EndPoint.Y;
              this.Line.X1 = 0;
              this.Line.Y2 = 0;
              this.Width = (line.EndPoint.X - line.StartPoint.X);
              this.Height = (line.StartPoint.Y - line.EndPoint.Y);

            }
            else
            {
              this.SetValue(InkCanvas.LeftProperty, line.StartPoint.X);
              this.SetValue(InkCanvas.TopProperty, line.StartPoint.Y);
              this.Line.Y1 = 0;
              this.Line.X1 = 0;
              this.Line.X2 = line.EndPoint.X - line.StartPoint.X;
              this.Line.Y2 = line.EndPoint.Y - line.StartPoint.Y;
              this.Width = (line.EndPoint.X - line.StartPoint.X);
              this.Height = (line.EndPoint.Y - line.StartPoint.Y);

            }

            // to prevent NaN bug in InkCanvas resizing code
            if (this.Width <= 0)
            {
              this.Width = 0.12345;
            }

            if (this.Height <= 0)
            {
              this.Height = 0.12345;
            }

            _sizeChanging = false;
            break;
          case WozPropertyType.Bounds:
            // don't know how right now
            break;

          default:
            // This shouldn't happen
            Debug.WriteLine(this.ToString() + " doesn't know what to do with a " + wozProperty.WozPropertyType.ToString());
            break;
        }
      }
      catch (Exception ex)
      {
        Debug.WriteLine("SketchLine: Property Setting Failed: " + ex.ToString());
      }
    }

    public void SetProperty(IEnumerable<WozLib.UI.Ink.SketchProperties.SketchProperty> wozProperties)
    {
      foreach (SketchProperty wozProperty in wozProperties)
      {
        SetProperty(wozProperty);
      }
    }

    // just return the specified property
    public SketchProperty GetProperty(WozPropertyType wozPropertyType)
    {
      return _WozPropertyCollection.GetProperty(wozPropertyType);
    }

    // WozPropertyCollection makes a copy for us
    public IEnumerable<SketchProperty> GetProperties()
    {
      return _WozPropertyCollection.WozProperties;
    }

    // Make sure we create the WozObject from the appropriate derived class (LineWozObject)
    public SketchObject GetWozObject()
    {
      return new LineSketchObject(WozID, GetProperties());
    }

    #endregion

    #region IWozObject Members


    public bool ShouldErase(Point pointRelativeToUie)
    {
      if (this.InputHitTest(pointRelativeToUie) != null)
      {
        return true;
      }
      else
      {
        return false;
      }
    }

    #endregion
  }
}
